package org.vison.wonfu.framework.configure;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.util.ContentCachingResponseWrapper;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import org.springframework.web.util.UrlPathHelper;
import org.vison.wonfu.framework.commons.Constants;

import javax.annotation.Resource;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;

@Component
@Slf4j
//@WebFilter(filterName = "logClientFilter", urlPatterns = "/**")
public class LogClientFilter implements Filter {

    private List<String> excludedUrls;

    @Resource
    private RequestContext requestContext;

    @Resource
    private ObjectMapper objectMapper;

    @Resource
    private WonfuAppProp wonfuAppProp;

    public void init(FilterConfig config) throws ServletException {
        log.debug("log client...");
        excludedUrls = Arrays.asList(
                "/exclude-url1",
                "/exclude-url2",
                "/exclude-url3"
        );
    }

    public void destroy() {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        String requestUrl = httpServletRequest.getRequestURI();

        // 检查请求的URL是否以"/actuator/"开头
        if (requestUrl.startsWith("/actuator/")) {
            // 如果请求的URL以"/actuator/"开头，则直接继续执行下一个过滤器或Servlet
            chain.doFilter(request, response);
            return;
        }
        String endPoint = new UrlPathHelper().getPathWithinApplication(httpServletRequest);
        //将'/'转为'.' 并去掉第一个'.'字符，例如"/api/v1/users"转为"api.v1.users"
        if (endPoint.startsWith("/")) {
            endPoint = endPoint.substring(1);
        }
        endPoint = endPoint.replace("/", ".");
        MDC.put(Constants.TRACE_ENDPOINT, endPoint);
        if (wonfuAppProp.isLocal()) {
            UUID uuid = UUID.randomUUID();
            MDC.put(Constants.TRACE_ID_STR, uuid.toString());
        }
        log.info("client 开始请求....");
        long startTime = new Date().getTime();
        HttpServletRequest requestObj;
        Map<String, Object> rootNode = new HashMap<>();
        ContentCachingResponseWrapper wrappedResponse = new ContentCachingResponseWrapper(httpServletResponse);
        boolean logEnabled = true;
        String contentType = Optional.ofNullable(httpServletRequest.getContentType()).orElse("").toLowerCase();
        if (contentType.startsWith("application/x-www-form-urlencoded")
                || contentType.startsWith("multipart/")
        ) {
            requestObj = httpServletRequest;
            logEnabled = false;
        } else {
            requestObj = new MultiReadHttpServletRequest(httpServletRequest);
        }
        rootNode.put("uri", requestObj.getRequestURI());
        String realClientIp = HttpUtils.getRealClientIp(httpServletRequest, true);
        rootNode.put("clientIp", realClientIp);
        requestContext.setRealClientIp(realClientIp);
        String method = requestObj.getMethod();
        rootNode.put("method", method);
        ObjectMapper mapper = new ObjectMapper();//先创建objmapper的对象
        if (method.equals("GET") || method.equals("DELETE")) {
            rootNode.put("request", httpServletRequest.getQueryString());
            String requestParam = mapper.writeValueAsString(rootNode);
            log.info("client 请求参数 {}", requestParam);
            chain.doFilter(requestObj, wrappedResponse);
        } else {
            if (logEnabled) {
                MultiReadHttpServletRequest wrappedRequest = (MultiReadHttpServletRequest) requestObj;
                String content = new String(wrappedRequest.getBody(), StandardCharsets.UTF_8);
                rootNode.put("request", content);
                JsonNode requestBody = objectMapper.readTree(content);
                requestContext.setRequestBody(requestBody);
                String requestParam = mapper.writeValueAsString(rootNode);
                log.info("client 请求参数 {}", requestParam);
                chain.doFilter(wrappedRequest, wrappedResponse);
            } else {
                chain.doFilter(requestObj, wrappedResponse);
                rootNode.put("request", convertToQueryString(requestObj.getParameterMap()));
                String requestParam = mapper.writeValueAsString(rootNode);
                log.info("client 请求参数 {}", requestParam);
            }
        }
        //必须在处理wrappedRequest.getContentAsByteArray()之前调用

        // Finally remember to respond to the client with the cached data.
        String responseContentType = httpServletResponse.getContentType();
        long endTime = new Date().getTime();
        long duration = endTime - startTime;
        if (responseContentType != null && responseContentType.contains("application/json")) {
            log.info("request 返回 {} 耗时 {} 毫秒", new String(wrappedResponse.getContentAsByteArray()),
                    duration);
        }
        MDC.remove(Constants.TRACE_ENDPOINT);
        wrappedResponse.copyBodyToResponse();
    }

    public String convertToQueryString(Map<String, String[]> parameters) {
        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        parameters.forEach((key, value) -> {
            params.add(key, value[0]);
        });
        UriComponents uriComponents = UriComponentsBuilder.newInstance()
                .queryParams(params).build();
        return uriComponents.toString();
    }

}